#!/usr/bin/python
#-----------------------------------------------------------------------------|
"""
Analyze module: Analyze transactions

Part of Expenses.
License GPL.
"""
__author__ = "ralfoide@gmail.com"

import re
import math

#------------------------
class Stats(object):
    def __init__(self, title):
        self._title = title
        self._finished = False
        self.sum = 0
        self.avg = 0
        self.min = None
        self.max = None
        self.dev = 0
        self.num = 0
        self._values = []

    def add(self, value):
        self._values.append(value)
        self.sum += value
        self.num += 1
        if self.min is None or value < self.min:
            self.min = value
        if self.max is None or value > self.max:
            self.max = value

    def finish(self):
        if self._finished: return
        self._finished = True
        if self.num == 0: return
        self.avg = self.sum / self.num
        
        std = 0
        avg = self.avg
        for v in self._values:
            std += (avg - v)**2
        self.dev = math.sqrt(std / self.num)

    def __str__(self):
        self.finish()
        return "%-16s: sum %9.2f, avg %9.2f, stddev %9.2f, min %9.2f, max %9.2f" % (
            self._title, self.sum, self.avg, self.dev, self.min, self.max)        

#------------------------
class Analyze(object):
    """
    Describe class
    """
    def __init__(self, log, accounts):
        self._log = log
        self._accounts = accounts
    
    def Dump(self):
        for k, v in self._accounts.iteritems():
            print "*** [%s] => %d items" % (k, len(v))
            for t in v:
                print "  ", str(t)

    def SumCategories(self, cats):
        filters = self._PrepareFilters(cats)

        sums = {}
        for t in self._AllTransactions():
            found = None
            for cat, is_regexp, r in filters:
                if is_regexp and r.search(t.name):
                    found = cat
                    break
                elif (not is_regexp) and t.value == r:
                    found = cat
                    break;
            if found:
                s = sums.get(found, { "sum": 0, "list": [] })
                if not found in sums:
                    sums[found] = s
                s["sum"] += t.value
                s["list"].append(t)
        # reformat the sums dict as a list, to preserve order
        result = [(name, sums.get(name, { "sum": 0, "list": [] })) for name, _ in cats]
        return result

    def _PrepareFilters(self, cats):
        """
        Returns list[ tuple( string: cat; boolean: T=regexp, F=value; float|regexp) ]
        """
        filters = [] 
        for name, r in cats:
            re_src = []
            for v in r.split("|"):
                if v and (v[0] == "-" or v[0] == "+"):
                    filters.append((name, False, float(v)))
                else:
                    re_src.append(v)
            if re_src:
                filters.append((name, True, re.compile("|".join(re_src), re.IGNORECASE)))
        return filters

    def _AllTransactions(self):
        for k, v in self._accounts.iteritems():
            for t in v:
                yield t
    
    def DumpSums(self, sums_list, baseline_cat, account_names):
        
        print
        print "----------------------"
        print "--- SUMS PER CAT  ----"
        print "----------------------"
        print
        
        total_trans = 0
        ref_value = -1.
        for cat, v in sums_list:
            if cat == baseline_cat:
                ref_value = v["sum"]
                break
        for cat, v in sums_list:
            print "*** [%s] => %9.2f   %6.1f%%" % (cat, v["sum"], 100.0 * v["sum"] / ref_value)
            for t in v["list"]:
                print "  ", t.str(account_names)
                total_trans += 1
        print "-- total trans: ", total_trans 

    def SumMonth(self, cats):
        filters = self._PrepareFilters(cats)

        months = {}

        for t in self._AllTransactions():
            found = None
            for cat, is_regexp, r in filters:
                if is_regexp and r.search(t.name):
                    found = cat
                    break
                elif (not is_regexp) and t.value == r:
                    found = cat
                    break;
            if found:
                key = t.date[0:6]
                month = months.get(key, {})
                if not key in months:
                    months[key] = month
                s = month.get(found, Stats("%s:%s" % (key, found)))
                if not found in month:
                    month[found] = s
                s.add(t.value)
        
        monthly_cats = []
        for name, _ in cats:
            s = Stats(name)
            monthly_cats.append(s)
            for month in months.itervalues():
                if name in month:
                    s.add(month[name].sum)
        
        return (months, monthly_cats)

    def DumpMonth(self, months, monthly_cats):

        print
        print "----------------------"
        print "--- SUMS PER MONTH ---"
        print "----------------------"
        print

        cats = []
        for s in monthly_cats:
            cats.append(s._title)
            print str(s)

        month_keys = months.keys()
        month_keys.sort()

        for month_key in month_keys:
            print
            month = months[month_key]
            for name in cats:
                if not name in month: continue
                print str(month[name])

        print "Done"            

#------------------------
# Local Variables:
# mode: python
# tab-width: 4
# py-continuation-offset: 4
# py-indent-offset: 4
# sentence-end-double-space: nil
# fill-column: 79
# End:
